home *** CD-ROM | disk | FTP | other *** search
/ Eagles Nest BBS 8 / Eagles_Nest_Mac_Collection_Disc_8.TOAST / Developer Tools⁄Additions / Protocols / YMODEM.C < prev   
Text File  |  1989-03-13  |  21KB  |  969 lines

  1. /*
  2.  * Copyright 1984,1985 Omen Technology Inc All Rights Reserved
  3.  * Ward Christensen Protocol handler for sending and receiving
  4.  * ascii and binary files.  Modified for choice of checksum or crc.
  5.  * This code may be used for developing YMODEM support ONLY IF
  6.  * the program documentation acknowledges the origin of this code.
  7.  */
  8.  
  9. #ifdef CPM
  10. EXT char defdisk;        /* Default disk */
  11. EXT char origdisk;        /* Disk originally logged into */
  12. #define PSTRLEN 33    /* For misc string params */
  13. #ifndef PATHLEN
  14. #define PATHLEN 65    /* Plenty long */
  15. #endif
  16. #endif
  17.  
  18. #define CTRL(v) ('v' & 037)
  19. #define ERROR (-1)
  20. #define BACKUP (-3)    /* Returned to expand to access prev file */
  21. #define NOTFOUND (-4)    /* Returned to expand to indicate can't open */
  22. #define FOUNDIT (-5)    /* Function found what it was looking for */
  23. #define OK 0
  24. #define TRUE 1
  25. #define FALSE 0
  26. #define SAYTERM 2    /* Doykbd returns this if sayterm needed */
  27. #define SECSIZ 128
  28. #define CPMEOF 0x1A
  29.  
  30. /*
  31.  *  Some important usage() error numbers
  32.  */
  33. #define SJ_ABORT 20    /* Keyboard abort via abort key */
  34.  
  35. EXT FILE *cfin,        /* Surrogate input getcty, kgets */
  36.     *fin,        /* For file uploads */
  37.     *fout,        /* For file downloads */
  38.     *cout,        /* For text capture and utility command redirection */
  39.     *fcall;        /* script reader */
  40.  
  41. EXT char cfast;        /* BDS C fastest access is to extern's */
  42. EXT char checksum;    /* Delcared here for speed */
  43. EXT unsigned oldcrc;    /* Accumulates CRC checksum */
  44. EXT int wcj, firstch, errors;
  45. EXT int firstsec;    /* First sector, C instead of NAK for crc */
  46.  
  47. #define CMDLEN 130
  48.  
  49. #define SOH 1
  50. #define STX 2
  51. #define ETX 3
  52. #define EOT 4
  53. #define ENQ 5
  54. #define ACK 6
  55. #define SO 016
  56. #define SI 017
  57. #define DLE 020
  58. #define XON 021
  59. #define XOFF 023
  60. #define NAK 025
  61. #define CAN 030
  62. #define ESC 033
  63. #define WANTCRC 0103    /* Send C not NAK to get crc not checksum */
  64. #define WANTG 0107    /* Send G not NAK to get nonstop batch xmsn */
  65. #define TIMEOUT (-2)
  66. #define WAICHR '\336'    /* Causes a wait in put[w] and answerback */
  67. #define RETRYMAX 10
  68. #define RETRYNOCRC 4    /* Drop CRC request after 4 retries */
  69. #define KSIZE    1024    /* Block length with k option */
  70.  
  71. /* Declare all globally used functions not returning int */
  72. char *index(), *cisubstr(), *stem();
  73. char *ascgettime(), *ascgetd(), *ascexpftime();
  74. char **gettoken();
  75. long fseek(), toutime(), time();
  76. unsigned updetime();
  77. FILE *xfopen();
  78.  
  79. This code has been modified from the Professional-YAM code with
  80. EXT unsigned Etime;        /* Elapsed time in seconds */
  81. EXT struct tsruct calltime;    /* Set when connection is made */
  82. EXT int Zone;            /* Zone in minutes from GMT */
  83. EXT int Thiszone;        /* Timezone to use for this command */
  84.  
  85. /*
  86.  * Structure passed by e1xpand to called function.
  87.  *  most just use the first string part (sorry, lint!)
  88.  */
  89. #define UFNSIZE 68            /* 64 byte pathlen plus drive */
  90. #define FNLENGTH 15            /* Directory filename */
  91. #define DONOMATCH 0x8000        /* Always expand pathspecs */
  92. struct expf {
  93.     char expfname[UFNSIZE];        /* ASCIZ file name */
  94.     long expflen;            /* File length in bytes */
  95.     unsigned exptime;
  96.     unsigned expdate;
  97.     char expfn[FNLENGTH];        /* Filename only no path */
  98.     char expfattr;            /* File attribute */
  99. };
  100.  
  101.  
  102. EXT char Cname[PATHLEN], Rname[PATHLEN], Tname[PATHLEN]; /* Saved filenames */
  103.  
  104. #define MAXPACK    94        /* Maximum Kermit packet size */
  105. #define RBUFL    200        /* Kermit Receive buffer length */
  106.  
  107.             /* Some declarations for USQ feature */
  108. #define SQMAGIC 0xFF76        /* SQueezed file prefix */
  109. #define KSQMAGIC 0xFF75        /* Cipher key magic word */
  110. #define KEYSIZE 4096        /* Max size of key file */
  111. #define NUMVALS 257        /* 256 data values plus SPEOF*/
  112.  
  113. extern union {        /* Decoding tree for usq feature */
  114.     struct {
  115.         char ffxpkt[MAXPACK+8];    /* Far fetch MUST BE FIRST */
  116.         char rxpkt[RBUFL+2];    /* Receive packet buffer */
  117.         char txpkt[MAXPACK+8];    /* Packet buffer */
  118.         char sxpkt[MAXPACK+8];    /* Server command buffer */
  119.         char dxpkt[MAXPACK+8];    /* Packet buffer */
  120.         char axbuf[CMDLEN+2];    /* For CB command, etc. */
  121.         char kfilnam[PATHLEN+2];
  122.     } k;
  123.     char ubuf[KSIZE+2];
  124.     struct {
  125.         int children[2];    /* Left, Right */
  126.     } dnode[NUMVALS - 1];
  127. } U;
  128.  
  129. EXT FLAG Key;            /* True iff unsqueezing encrypted file */
  130.  
  131.  
  132.  
  133. expanations added for support routines not included.
  134.  
  135. #include "yamsys.h"        /* Installation specific stuff */
  136. #include "yam.h"
  137. #define WCEOT (-10)
  138.  
  139. long Modtime;        /* Unix style mod time for incoming file */
  140. int Usemtime;        /* <>0: Accept file mod time */
  141. int Filemode;        /* Unix style mode for incoming file */
  142. char rxcmdchar;        /* NAK, C, or G to specify rx mode */
  143. extern char *Rcmdlog;    /* Remote commands logged to this file */
  144. static FLAG eotseen;    /* <>0 if an EOT has been seen for this rx packet */
  145.  
  146. wcsend(argc, argp)
  147. char **argp;
  148. {
  149.     int wcs();
  150.  
  151.     Crcflg = FALSE;
  152.     firstsec = TRUE;
  153.     if (Batch) {
  154.         lhmargin();
  155.         printf("Sending in Batch Mode\n");
  156. /* Expand calls the specified function once for each file, with that
  157. pathname as argument */
  158.         if (expand(wcs, argc, argp, 0) == ERROR)
  159.             goto fubar;
  160.         if (wctxpn("") == ERROR)
  161.             goto fubar;
  162.     }
  163.     else {
  164.         for (; --argc>=0;) {
  165. /* Opentx opens a file for sending - `ala fopen(name, "rb" .. */
  166.             if (opentx(*argp++) == ERROR)
  167.                 goto fubar;
  168.             if (wctx() == ERROR)
  169.                 goto fubar;
  170.         }
  171.     }
  172.     return OK;
  173. fubar:
  174.     closetx('E'); ++Errcnt;
  175.     canit();
  176.     return ERROR;
  177. }
  178.  
  179. wcs(ufn)
  180. register struct expf *ufn;
  181. {
  182.     if (opentx(ufn->expfname) == ERROR)
  183.         return OK;        /* skip over inaccessible files */
  184.     if (wctxpn(ufn) ==  ERROR)
  185.         return ERROR;
  186.     if (wctx() == ERROR)
  187.         return ERROR;
  188.     return OK;
  189. }
  190.  
  191.  
  192. wcreceive(argc, argp)
  193. char **argp;
  194. {
  195.     rxcmdchar = NAK;
  196.     if (Batch || argc == 0)
  197.         Crcflg = Batch = TRUE;
  198.     if (Crcflg)
  199.         rxcmdchar = WANTCRC;
  200.     if (Optiong > 0)
  201.         Crcflg = rxcmdchar = WANTG;
  202.  
  203.     if (Batch) {
  204.         lhmargin(); Batch = TRUE;
  205.         printf("Receiving in Batch Mode\n");
  206.         if (argc)
  207.             usage(11);
  208.         for (;;) {
  209.             if (wcrxpn(U.ubuf)== ERROR)
  210.                 goto fubar;
  211.             if (U.ubuf[0] == 0) {
  212.                 pstat("");  return OK;
  213.             }
  214.             procheader(U.ubuf);
  215.  
  216.             if (wcrx(U.ubuf) == ERROR)
  217.                 goto fubar;
  218.         }
  219.     }
  220.     else {
  221.         procheader(NULL);
  222.         if (Xmodem)
  223.             printf("Receive:'%s' FILE OPEN\n", *argp);
  224.         if (wcrx(*argp) == ERROR)
  225.             goto fubar;
  226.     }
  227.     Bytesleft = DEFBYTL; return OK;
  228. fubar:
  229.     Bytesleft = DEFBYTL; canit(); closerx(eotseen?'Q':ERROR);
  230.     ++Errcnt; return ERROR;
  231. }
  232.  
  233. /*
  234.  * Fetch a pathname from the other end as a C ctyle ASCIZ string.
  235.  * Length is indeterminate as long as less than blklen
  236.  * a null string represents no more files
  237.  */
  238. wcrxpn(rpn)
  239. char *rpn;    /* receive a pathname */
  240. {
  241.     purgeline();
  242.     Crcflg = firstsec = TRUE;
  243.     lpstat("Fetching pathname");
  244.     totsecs = -1;
  245.     if (wcgetsec(rpn, rxcmdchar) != 0)
  246.         return ERROR;
  247.     /* Send the ACK unless g option */
  248.     if (Optiong <= 0)
  249.         sendline(ACK);
  250.     FLUSHMO;
  251.     return OK;
  252. }
  253.  
  254. wctxpn(ufn)
  255. struct expf *ufn;
  256. {
  257.     register char *p, *q;
  258.     int i;
  259. #ifdef MTIME
  260.     long mdt; struct dstruct d; struct tsruct t;
  261. #endif
  262.     totsecs = -1;
  263.     pstat("Awaiting pathname NAK");
  264.     if (getnak())
  265.         return ERROR;
  266.     for (i = KSIZE, q = U.ubuf;  --i >= 0;  )
  267.         *q++ = 0;
  268.     p = ufn->expfname;
  269.     if (*p) {
  270. #ifdef MTIME
  271.         tdexpftime(&d, &t, ufn);
  272.         mdt = toutime(&d, &t) + Thiszone*60L;
  273. #endif
  274.         if (!Fullpath) {
  275.             p = stem(p);
  276.             uncaps(p);
  277.         }
  278.         for(q = U.ubuf; *p; )    /* don't send drive: */
  279.             if((*q++ = *p++) == ':')
  280.                 q = U.ubuf;
  281.         /* transmit file length */
  282. #ifdef MTIME
  283.         sprintf(++q, "%ld %lo 0 %ld", ufn->expflen, mdt, Serialn);
  284. #else
  285.         sprintf(++q, "%ld 0 0 %ld", ufn->expflen, Serialn);
  286. #endif
  287.     }
  288.  
  289.     if (wcputsec(U.ubuf, 0, SECSIZ) == ERROR) {
  290.         wcperr("Can't send pathname %s", p);
  291.         return ERROR;
  292.     }
  293.     return OK;
  294. }
  295.  
  296. getnak()
  297. {
  298.     register c;
  299.  
  300.     Lastrx = 0;
  301.     for (;;) {
  302.         FLUSHMO;
  303.         switch (firstch = readline(400)) {
  304.         case TIMEOUT:
  305.             return TRUE;
  306.         case WANTG:
  307.             Optiong = 1; blklen = 1024;
  308.             /* **** FALL THRU TO **** */
  309.         case WANTCRC:
  310.             Crcflg = TRUE;
  311.             /* **** FALL THRU TO **** */
  312.         case NAK:
  313.             if ((c=readl0()) == TIMEOUT)
  314.                 return FALSE;
  315.             if (c == 'K') {        /* IMP hack */
  316.                 blklen = 1024; return FALSE;
  317.             }
  318.             /* **** FALL THRU TO **** */
  319.         case CAN:
  320.             if (Lastrx == CAN)
  321.                 return TRUE;
  322.             /* **** FALL THRU TO **** */
  323.         default:
  324.             showctl(firstch);
  325.         }
  326.         Lastrx = firstch;
  327.     }
  328. }
  329.  
  330. /*
  331.  * Adapted from CMODEM13.C, written by
  332.  * Jack M. Wierda and Roderick W. Hart
  333.  */
  334.  
  335. wcrx(name)
  336. char *name;
  337. {
  338.     register char *p;
  339.     register cblklen;            /* bytes to dump this block */
  340.     int sendchar, sectnum, sectcurr;
  341.     int syntry;            /* number of tries to get right sec */
  342.  
  343.     if (openrx(name) == ERROR)
  344.         return ERROR;
  345.     firstsec = TRUE; eotseen = FALSE;  totsecs = sectnum = 0;
  346.     sendchar = rxcmdchar;
  347.  
  348.     for (;;) {
  349.         syntry = 0;        /* # of tries for right sector */
  350. synagain:
  351.         sectcurr = wcgetsec(U.ubuf, sendchar);
  352.         if (sectcurr == (sectnum+1 & 0377)) {
  353.             sectnum++;
  354. /*
  355.  * if the compiler supports longs && the o/s records the
  356.  *  exact length of files then and only then use the file length
  357.  *  info (if transmitted).
  358.  */
  359.             wcj = cblklen = Bytesleft>blklen ? blklen:Bytesleft;
  360.  
  361.             Charsrx += wcj;
  362.             if (Overlapio) {
  363.                 if (Optiong <= 0)
  364.                     sendline(ACK);
  365.                 sendchar = -1;
  366.             } else
  367.                 sendchar=ACK;
  368.             FLUSHMO;
  369.             for (p = U.ubuf; --wcj>=0; )
  370.                 if (putc(*p++, fout) == ERROR)
  371.                     usage(30);
  372.             if (View) {
  373.                 wcj = cblklen;
  374.                 for (p = U.ubuf;--wcj>=0;)
  375.                     bttyout(*p++);
  376.             }
  377.             if ((Bytesleft -= cblklen) < 0)
  378.                 Bytesleft = 0;
  379.             totsecs += blklen/128;
  380.         }
  381.         else if (sectcurr == (sectnum & 0377)) {
  382.             wcperr("Received dup Sector");
  383.             sendchar = ACK;
  384.         }
  385.         else if (sectcurr == WCEOT) {
  386.             sendline(ACK);
  387.             FLUSHMO;
  388.             /* Don't pad the file any more than it already is */
  389.             closerx('R');
  390. #ifdef MTIME
  391.             if (Modtime) {
  392.                 sutime(Rname, Modtime - Thiszone*60L);
  393.                 Modtime = 0L;
  394.             }
  395. #endif
  396.             return OK;
  397.         }
  398.         else if (sectcurr == ERROR)
  399.             return ERROR;
  400.         else {
  401.             wcperr("Sync Error: got %d", sectcurr);
  402.             if (++syntry < 12) {
  403.                 sendchar = Batch ? WANTCRC : NAK;
  404.                 goto synagain;
  405.             }
  406.             return ERROR;
  407.         }
  408.     }
  409. }
  410.  
  411. /*
  412.  * wcgetsec fetches a Ward Christensen type sector.
  413.  * Returns:
  414.  *  sector number encountered
  415.  *  or ERROR if valid sector not received, or CAN CAN received
  416.  *  or WCEOT if eot sector
  417.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  418.  *    (Caller must do that when he is good and ready to get next sector)
  419.  */
  420.  
  421. wcgetsec(rxbuf, sendchar)
  422. char *rxbuf;
  423. {
  424.     register char *p;
  425.     register c;
  426.     register sectcurr;
  427.     register maxtim = 100;
  428.  
  429.     eotseen = FALSE;
  430.     for (Lastrx = errors = 0; errors < RETRYMAX;  ++errors, ++toterrs) {
  431.         if(Ctrlbrk)
  432.             return ERROR;
  433.  
  434.         if (firstsec && !Batch && errors >= RETRYNOCRC ) {
  435.             sendchar = NAK;  Crcflg = FALSE;
  436.         }
  437.         showsec();
  438.         if (sendchar > 0) {
  439.             sendline(sendchar);    /* Send it now, we're ready! */
  440.         }
  441.         FLUSHMO;
  442.         blklen = SECSIZ;
  443. noisereject:
  444.         switch (c = readline(maxtim)) {
  445.         case CAN:
  446.             if (Lastrx == CAN) {
  447.                 wcperr("Sender CANcelled");
  448.                 return ERROR;
  449.             } else {
  450.                 Lastrx = CAN;
  451.                 continue;
  452.             }
  453.         case EOT:
  454.             if (Optiong)
  455.                 return WCEOT;
  456.             ++eotseen;
  457.             /* make sure eot really is eot and not just mixmash */
  458.             if (readl0() == TIMEOUT) {
  459.                 if (!Crcflg || Lastrx == EOT)
  460.                     return WCEOT;
  461.                 Lastrx = EOT;  goto bilge3;
  462.             }
  463.             goto bilge;
  464.         case STX:
  465.             blklen = KSIZE;
  466.         case SOH:
  467.             sectcurr = readline(50);
  468.             if ((sectcurr+readline(50)) == 0377) {
  469.                 checksum = oldcrc = 0;
  470.                 for (p = rxbuf,wcj = blklen; --wcj>=0; ) {
  471.                     if ((c = readline(50)) < 0)
  472.                         goto bilge;
  473.                     oldcrc = updcrc(c, oldcrc);
  474.                     checksum += (*p++ = c);
  475.                 }
  476.                 if ((c = readline(50)) < 0)
  477.                     goto bilge;
  478.                 if (Crcflg) {
  479.                     oldcrc = updcrc(c, oldcrc);
  480.                     if ((c = readline(50)) < 0)
  481.                         goto bilge;
  482.                     oldcrc = updcrc(c, oldcrc);
  483.                     if (oldcrc) {
  484.                         wcperr("Bad CRC=%04x",
  485.                           oldcrc);
  486.                         goto bilge2;
  487.                     }
  488.                     else {
  489.                         firstsec = FALSE;
  490.                         return sectcurr;
  491.                     }
  492.                 }
  493.                 else if ((checksum-c) & 0377) {
  494.                     wcperr("Checksum Bad rx=%02x cx=%02x",
  495.                      c, checksum);
  496.                     goto bilge2;
  497.                 } else {
  498.                     firstsec = FALSE;
  499.                     return sectcurr;
  500.                 }
  501.             }
  502.             wcperr("Sector number garbled"); goto bilge;
  503.         default:
  504.             if (Crcflg)
  505.                 goto noisereject;
  506.         case TIMEOUT:
  507.             break;
  508.         }
  509. bilge:
  510.         switch (c) {
  511.         case TIMEOUT:
  512.             wcperr("Timeout"); goto bilge3;
  513.         case ERROR:  case (ERROR & ~0200):
  514.             Mcstat = 0;
  515.             wcperr("Modem SR=%02x", Mcstat);
  516.             break;
  517.         default:
  518.             wcperr("Got %02x sector header", c);
  519.             break;
  520.         }
  521. bilge2:
  522.         junkpacket();
  523. bilge3:
  524.         if (firstsec)
  525.             sendchar = rxcmdchar;
  526.         else {
  527.             maxtim = 50;
  528.             sendchar = NAK;
  529.         }
  530.     }
  531.     /* try to stop the bubble machine. */
  532.     canit(); return ERROR;
  533. }
  534.  
  535. /*VARARGS1*/
  536. wcperr(s,p,q)
  537. char *s, *p, *q;
  538. {
  539.     char bufx[70];
  540.  
  541.     llhmargin();
  542.     sprintf(bufx, s, p, q);
  543.     lprintf("Sector %3d error %d: %s\n", totsecs, errors, bufx);
  544. #ifdef DEBUG2
  545.     if ( !Serialn) {
  546.         logfile(Rcmdlog, bufx, firstch, (long)totsecs);
  547.         if (Xmodem && !carrier())
  548.             logfile(Rcmdlog, "CARRIER LOST", 'C', (long)updetime());
  549.     }
  550. #endif
  551. }
  552.  
  553. showsec()
  554. {
  555.     if (!Quiet)
  556.         pstat("Sector %3d %2dk %s",
  557.           totsecs, totsecs/8, Crcflg?"CRC-16":"" );
  558. }
  559.  
  560. wctx()
  561. {
  562.     register char *p;
  563.     register unsigned sectnum;
  564.  
  565.     firstsec = TRUE; totsecs = 0;
  566.     pstat("Awaiting initial NAK");
  567.  
  568.     if (getnak())
  569.         return ERROR;
  570.     sectnum = 1;
  571.     while (filbuf(U.ubuf, blklen)) {
  572.         totsecs += (blklen/128);
  573.         showsec();
  574.         if (wcputsec(U.ubuf, sectnum, blklen) == ERROR) {
  575. #ifdef DEBUG2
  576.             if ( !Serialn)
  577.                 logfile(Rcmdlog, "SE1", firstch, Charstx);
  578. #endif
  579.             if (firstch == WANTCRC && sectnum > 1) {
  580.                 long lseek();
  581.  
  582.                 if (lseek(fileno(fin), -blklen*2L, 1) == -1L)
  583.                     return ERROR;
  584.                 --sectnum; Charstx -= blklen;
  585.                 totsecs -= (blklen/64);
  586.                 wcperr("Resynchronizing");
  587.                 continue;
  588.             }
  589.             return ERROR;
  590.         } else {
  591.             if (View)
  592.                 for (p = U.ubuf,wcj = blklen;--wcj>=0;)
  593.                     bttyout(*p++);
  594.             ++sectnum; Charstx += blklen;
  595.         }
  596.     }
  597.     closetx('S');
  598.     for (errors = 0; ++errors<RETRYMAX; ) {
  599.         purgeline();
  600.         sendline(EOT);
  601.         FLUSHMO;
  602.         if (Ctrlbrk)
  603.             break;
  604.         if((firstch=readline(100)) == ACK || firstch == (ACK|0200))
  605.             return OK;
  606.         wcperr("Got %02x for ACK to EOT", firstch);
  607.     }
  608.     wcperr("No ACK on EOT");
  609.     return ERROR;
  610. }
  611.  
  612. wcputsec(txbuf, sectnum, cseclen)
  613. char *txbuf;
  614. register unsigned sectnum;
  615. int cseclen;    /* data length of this sector to send */
  616. {
  617.     register char *p;
  618.     FLAG nogood;
  619.  
  620.     firstch = 0;    /* part of logic to detect CAN CAN */
  621.  
  622.     for (errors = 0; errors < RETRYMAX; ++errors, ++toterrs) {
  623.         nogood = FALSE;
  624.         if (Ctrlbrk)
  625.             goto cancan;
  626.         Lastrx =  firstch;
  627.         sendline(cseclen == KSIZE?STX:SOH);
  628.         sendline(sectnum);
  629.         sendline(-sectnum-1);
  630.         oldcrc = checksum = 0;
  631.         for (wcj = cseclen,p = txbuf; --wcj>=0; ) {
  632.             sendline(*p);
  633.             oldcrc = updcrc(*p, oldcrc);
  634.             checksum += *p++;
  635.             if (miinqueue()) {
  636.                 switch (michar()) {
  637.                 case CAN:
  638.                     goto chkcan;
  639.                 case XOFF:
  640.                     readline(500);  break;
  641.                 default:
  642.                     break;
  643.                 }
  644.             }
  645.         }
  646.         if (Crcflg) {
  647.             oldcrc = updcrc(0,updcrc(0,oldcrc));
  648.             sendline(oldcrc>>8);sendline(oldcrc);
  649.         }
  650.         else
  651.             sendline(checksum);
  652.         if (Ctrlbrk)
  653.             goto cancan;
  654.  
  655.         if (miready()) {
  656.             wcperr("Noise Burst Detected");
  657.             if (Optiong > 0)
  658.                 goto cancan;
  659.             nogood = TRUE; purgeline();
  660.         }
  661.         if (Optiong) {
  662.             firstsec = FALSE; return OK;
  663.         }
  664.         FLUSHMO;
  665.         firstch = readline(400);
  666. gotnak:
  667.         switch (firstch) {
  668.         case CAN:
  669. chkcan:
  670.             if(Lastrx == CAN) {
  671. cancan:
  672.                 wcperr("Receiver Cancelled");  return ERROR;
  673.             }
  674.             break;
  675.         case TIMEOUT:
  676.             wcperr("Timeout on sector ACK"); continue;
  677.         case WANTCRC:
  678.             if (firstsec)
  679.                 Crcflg = TRUE;
  680.         case NAK:
  681.             wcperr("NAK on sector");
  682. #ifdef NOTDEF
  683.             /* ******* TESTING ********** */
  684.             {
  685.                 extern Quitafter;
  686.                 if (Quitafter && Verbose) {
  687.                     wcperr("Faking ACK: ALT-Q");
  688.                     Quitafter = FALSE; return OK;
  689.                 }
  690.             }
  691. #endif
  692.             continue;
  693.         case ACK: 
  694. gotack:
  695.             if (nogood)
  696.                 break;
  697.             if (!Tfile || (sectnum==0) || (readl0()==TIMEOUT)) {
  698.                 firstsec = FALSE; return OK;
  699.             }
  700.             wcperr("Got burst for sector ACK");
  701.             break;
  702.         case ACK|0200: 
  703.             if ( !Tfile)
  704.                 goto gotack;
  705.         default:
  706.             wcperr("Got %02x for sector ACK", firstch); break;
  707.         }
  708.         for (;;) {
  709.             Mcstat = 0;
  710.             Lastrx = firstch;
  711.             if ((firstch = readline(400)) == TIMEOUT)
  712.                 break;
  713.             if ((firstch == NAK || firstch == WANTCRC)
  714.               && readl0() == TIMEOUT)
  715.                 goto gotnak;
  716.             if (firstch == CAN && Lastrx == CAN)
  717.                 goto cancan;
  718.             /* Let user see it if strange char */
  719.             showctl(firstch);
  720.         }
  721.     }
  722.     wcperr("No ACK on sector");
  723.     return ERROR;
  724. }
  725.  
  726. /* Throw away incoming packet */
  727. junkpacket()
  728. {
  729.     register n = 3600;
  730.     register c;
  731.     register b;
  732.  
  733.     b = Ctrlbrk; Ctrlbrk = 0;
  734.     pstat("%sError Recovery", Outarev);
  735.     while (--n) {
  736.         if (cdo()) {
  737.             prcdo(); goto done;
  738.         }
  739.         switch (c = readline(20)) {
  740.         case TIMEOUT:
  741.             goto done;
  742.         case CAN:
  743.         case ETX:
  744.             if (c == readline(10)) {
  745.                 b=c; goto done;
  746.             }
  747.         }
  748.     }
  749. done:    Ctrlbrk |= b;  Mcstat = 0;
  750.     pstat("");
  751. }
  752.  
  753.  
  754. /*
  755.  *  Send 5 CAN's to try to get the other end to shut up
  756.  *  then 5 backspaces to edit out the CAN's
  757.  */
  758. canit()
  759. {
  760.     register c;
  761.  
  762.     junkpacket();
  763.     for (c = 5; --c>=0; )
  764.         sendline(CAN);
  765.     for (c = 5; --c>=0; )
  766.         sendline('\b');
  767.     FLUSHMO;
  768. }
  769.  
  770. /* Fill buf with count chars padding with ^Z for CPM */
  771. filbuf(buf, count)
  772. char *buf;
  773. {
  774.     register m;
  775.  
  776. #ifdef NOTDEF
  777.     register c;
  778.     m = count;
  779.     while((c=getc(fin))!=EOF) {
  780.         *buf++ =c;
  781.         if(--m == 0)
  782.             break;
  783.     }
  784.     if(m == count)
  785.         return 0;
  786.     else
  787.         while(--m >= 0)
  788.             *buf++ = 032;
  789.     return count;
  790. #else
  791.     m = read(fileno(fin), buf, count);
  792.     if (m <= 0)
  793.         return 0;
  794.     while (m < count)
  795.         buf[m++] = 032;
  796.     return count;
  797. #endif
  798. }
  799.  
  800.  
  801. /*
  802.  * Process incoming header
  803.  */
  804. procheader(name)
  805. register char *name;
  806. {
  807.     register char *p;
  808.     long theirsn;
  809.     extern char Instmsg[];
  810.  
  811.     /* set default parameters */
  812.     Bytesleft = DEFBYTL;  Filemode = 0666; Modtime = 0L;
  813.  
  814.     if (name) {
  815.         p = name + 1 + strlen(name);
  816.         if (*p) {     /* file coming from Unix type system */
  817.             sscanf(p, "%ld%lo%o%lo", &Bytesleft,
  818.              &Modtime, &Filemode, &theirsn);
  819. #ifndef DEMO
  820.             if (Serialn && theirsn == Serialn) {
  821.                 Instmsg[0] = 0; usage(26);
  822.             }
  823. #endif
  824.         }
  825.     }
  826. }
  827.  
  828. /*
  829.  *  Routines to convert to/from Unix(TM) 32 bit time code (epoch:1970).
  830.  *  Good for times 1970 to 2035 approximately, by which time we should
  831.  *  be timestamping files in microseconds since the Big Bang ...
  832.  *
  833.  *  Original factor routine from  Lynn Long
  834.  */
  835.  
  836. #define EPOCH 719542L
  837.  
  838. long int factor(mm, dd, yy)
  839. long int mm, dd, yy;
  840. {
  841.     long int raw;    
  842.  
  843.     raw = 365 * yy + dd -1 + 31 * (mm - 1);
  844.     if(mm < 3)
  845.         raw += ((yy -1)/4);
  846.     else
  847.         raw -= (23 + 4*mm)/10L - yy/4;
  848.     return(raw);
  849. }
  850.  
  851. #ifdef MTIME
  852.  
  853.  
  854. struct dstruct {        /* Structure filled in by msdate() */
  855.     int year;
  856.     char month;
  857.     char day;
  858. };
  859. struct tsruct {            /* Structure filled in by mstod() */
  860.     char hrs;
  861.     char mins;
  862.     char secs;
  863.     char csecs;
  864. };
  865.  
  866. static dayinmo[]= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  867.  
  868. #define DAYSEC 86400L
  869. #define DAYPYR(y) (((y&03)) ? 365:366)
  870. /*
  871.  * Unpacks Unix format time into dstruct and tsruct structures
  872.  */
  873.  
  874. unutime(ds, ts, t)
  875. register struct dstruct *ds;
  876. register struct tsruct *ts;
  877. long t;
  878. {
  879.     register int c;
  880.     register long days, rem, y, mo;
  881.  
  882.     rem = t % DAYSEC;  days = t / DAYSEC;
  883.     ts->csecs = 0;
  884.     ts->secs = rem % 60L; rem /= 60L;
  885.     ts->mins = rem % 60L; ts->hrs = rem/60L;
  886.  
  887.     for (y=1970; days>=(c=DAYPYR(y)); ++y)
  888.         days -= c;
  889.  
  890.     dayinmo[1] = c==366 ? 29:28;
  891.  
  892.     for (mo=0; days>=(c=dayinmo[mo]); ++mo)
  893.         days -= c;
  894.     
  895.     ds->day = days+1; ds->month = mo+1; ds->year = y - 1980;
  896. #ifdef DEBUG
  897.     lprintf("Unutime: da %d mo %d yr %d %d:%d:%d\n",
  898.      ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs);
  899. #endif
  900. }
  901.  
  902.  
  903. /*
  904.  * Packs dstruct and tsruct structures into Unix format time
  905.  */
  906.  
  907. long
  908. toutime(ds, ts)
  909. register struct dstruct *ds;
  910. register struct tsruct *ts;
  911. {
  912.     register long t;
  913.  
  914.     t = factor((long)ds->month, (long)ds->day, ds->year + 1980L);
  915.     t -= EPOCH; t *= DAYSEC;
  916.     t += ts->secs; t += ts->mins*60; t += ts->hrs*3600L;
  917. #ifdef DEBUG
  918.     lprintf("Toutime: da %d mo %d yr %d %d:%d:%d %lo\n",
  919.      ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs, t);
  920. #endif
  921.     return t;
  922. }
  923.  
  924. /*
  925.  * Sets file s to Unix time t
  926.  */
  927. sutime(s, ti)
  928. char *s;
  929. long ti;
  930. {
  931.     struct dstruct ds;
  932.     struct tsruct ts;
  933.     register unsigned d, t;
  934.  
  935.     unutime(&ds, &ts, ti);
  936.  
  937.     d = ds.day + (ds.month << 5) + (ds.year << 9);
  938.     t = (ts.secs >> 1) + (ts.mins << 5) + (ts.hrs << 11);
  939. #ifdef DEBUG
  940.     lprintf("Sutime: d=%x t=%x", d, t);
  941.     lprintf(" da %d mo %d yr %d %d:%d:%d\n",
  942.      ds.day, ds.month, ds.year, ts.hrs, ts.mins, ts.secs);
  943. #endif
  944.     if (Usemtime)
  945.         utime(s, d, t);
  946. }
  947.  
  948. /*
  949.  * Unpack time from struct expf to tsruct and dstruct
  950.  */
  951. tdexpftime(ds, ts, e)
  952. register struct expf *e;
  953. register struct dstruct *ds;
  954. register struct tsruct *ts;
  955. {
  956.     ts->hrs = (e->exptime>>11);
  957.     ts->mins = ((e->exptime>>5)&077);
  958.     ts->secs = ((e->exptime&037)<<1);
  959.     ds->month = ((e->expdate>>5)&017);
  960.     ds->day = (e->expdate&037);
  961.     ds->year = ((e->expdate>>9)%100);
  962. #ifdef DEBUG
  963.     lprintf("TDEX.. da %d mo %d yr %d %d:%d:%d\n",
  964.      ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs);
  965. #endif
  966. }
  967.  
  968. #endif    /* M T I M E */
  969.